Skip to content

[hotfix][Optimizer] Recognize CLOSED optimizing processes - #4331

Open
czy006 wants to merge 2 commits into
apache:masterfrom
czy006:codex/hotfix-optimizer-closed-status
Open

[hotfix][Optimizer] Recognize CLOSED optimizing processes#4331
czy006 wants to merge 2 commits into
apache:masterfrom
czy006:codex/hotfix-optimizer-closed-status

Conversation

@czy006

@czy006 czy006 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Brief change log

Make OptimizingProcess.isClosed() recognize the CLOSED status set by close(), in addition to KILLED. This restores the guard against accepting late results for a closed optimizing process.

How was this patch tested?

  • Extend process-close queue coverage to assert the closed predicate.
  • Add screenshots for manual tests if appropriate (not applicable: backend-only change).
  • Run TestOptimizingQueue#testProcessCloseKeepsLastOptimizedSnapshotId locally with JDK 11 before creating this pull request.

Documentation

  • Does this pull request introduce a new feature? (yes / no)
  • no
  • If yes, how is the feature documented? (not applicable / docs / JavaDocs / not documented)
  • not applicable

@czy006
czy006 marked this pull request as ready for review August 17, 2026 09:11
@github-actions github-actions Bot added the module:ams-server Ams server module label Aug 17, 2026
close() sets ProcessStatus.CLOSED while isClosed() only compared
against KILLED - a status never assigned in this class - so the
predicate was permanently false and acceptResult's
OptimizingClosedException guard against late results on a closed
process could never fire (downstream isProcessing conditions happened
to contain the fallout).

Check both CLOSED and KILLED. Reachability analysis in the fix record:
close(false) cancels tasks so late completes fail earlier with
IllegalTaskStateException, and the partial-commit path legitimately
accepts late results; no public-API path drives the guard today, so
this lands as an intent fix verified by the full queue regression
(47/47) rather than a behavioral red test.
Fix record: docs/fix-records/2026-08-16-fix-22-is-closed-status-mismatch.md
@czy006
czy006 force-pushed the codex/hotfix-optimizer-closed-status branch from 65aadac to 96a97b2 Compare August 18, 2026 02:16
@czy006
czy006 requested a review from xxubai August 18, 2026 06:16
@@ -775,7 +775,10 @@ private void resetTask(TaskRuntime<RewriteStageTask> taskRuntime) {

@Override
public boolean isClosed() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering why we need isClosed() if we already have getStatus(). If it represents something other than the process status, we should either rename it to make its semantics clearer or remove it and use getStatus() instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the question. isClosed() is not a shortcut for getStatus() == ProcessStatus.CLOSED — it is the predicate "this process was terminated out-of-band and must reject late task results", and it intentionally spans two statuses:

  • CLOSED — written by close(false) (e.g. the table is released from the queue / the optimizer group changes) and by the partial-commit completion path in commit();
  • KILLED — the terminal state this class already treats as "process is gone" in poll() (status != KILLED && status != FAILED), and the recovery constructor restores the status from the persisted TableProcessMeta, so it can carry whatever the process framework persisted.

That is why simply removing it and comparing getStatus() at the call site does not express the intent:

  • comparing getStatus() == ProcessStatus.CLOSED would silently lose the KILLED arm;
  • comparing getStatus() == CLOSED || getStatus() == KILLED just re-creates this predicate inline, without a name.

For context, this is also exactly how the bug was introduced: #3257 migrated the old OptimizingProcess.Status check correctly (status == ProcessStatus.CLOSED), then #3486 changed the readers (poll(), the recovery check, isClosed()) from CLOSED to KILLED while leaving close() writing CLOSED. Since nothing in this class assigns KILLED, isClosed() has been permanently false and the guard in acceptResult() dead ever since. Keeping one named predicate for "results are no longer accepted" — instead of scattered raw status comparisons — is what prevents this kind of writer/reader drift from recurring.

On the naming — fair point that isClosed() covering KILLED can read as a CLOSED shortcut at first glance. I'd like to keep this hotfix minimal (just restoring the dead guard) and treat any rename (e.g. isTerminated()) as a follow-up if you think it is worth it, since it would touch the public OptimizingProcess interface.

@czy006
czy006 requested a review from xxubai August 21, 2026 02:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module:ams-server Ams server module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants